package top.ninwoo.cloudcenter.register;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.zookeeper.CreateMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import top.ninwoo.common.EdgeNodeEntity;
import top.ninwoo.common.entity.NetworkTopology;
import top.ninwoo.utils.util.impl.IpUtils;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class CloudRegisterCenter implements Register {

    private final static Logger LOG = LoggerFactory.getLogger(CloudRegisterCenter.class);

    private final static String hostAddress = IpUtils.getHostIp("10.");

    // 用于存储全部可用的集群信息
    private ConcurrentHashMap<String, EdgeNodeEntity> availableEdgeNodes = new ConcurrentHashMap<>();

    @Value("${bs.cloudcenter.name}")
    private String cloudCenterName;

    @Value("${server.port}")
    private int port;

    @Resource
    private CuratorFramework zkClient;

    @Resource
    private RestTemplate restTemplate;

    // 边缘计算节点的监控器
    private PathChildrenCache edgeNodesWatcher = null;

    @PostConstruct
    public void init() {
        if("".equals(cloudCenterName)) {
            cloudCenterName = "default-cloud-center-name";
        }
        // 注册节点
        registerNode(cloudCenterName);

        // 注册监听节点
        edgeNodesWatcher = createEdgeNodesWatcher(cloudCenterName);
    }

    @PreDestroy
    public void close() {
        if(edgeNodesWatcher != null) {
            try {
                edgeNodesWatcher.close();
            } catch (IOException e) {
                LOG.warn("监听器关闭失败");
                e.printStackTrace();
            }
        }
    }

    @Override
    public void registerNode(final String nodeId) {
        try {
            List<String> homeNodes = zkClient.getChildren().forPath("/");
            if(!homeNodes.contains(nodeId)) {
                LOG.info("创建初始化的云节点");
                zkClient.create()
                        .withMode(CreateMode.PERSISTENT) // 这里选择创建持久节点
                        .forPath("/" + nodeId, hostAddress.getBytes());
            } else {
                // 更新IP地址
                LOG.info("Cloud 地址更新");
                zkClient.setData().forPath("/" + nodeId, (hostAddress + ":" + port).getBytes());
            }
        } catch (Exception e) {
            LOG.error("创建cloud节点{}失败", nodeId);
            e.printStackTrace();
        }
    }

    /**
     * 创建节点监听器
     * @param nodeId
     * @return
     */
    public PathChildrenCache createEdgeNodesWatcher(final String nodeId) {
        PathChildrenCache watcher = new PathChildrenCache(zkClient, "/" + nodeId, true);
        watcher.getListenable().addListener((client, event) -> {
            if(event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED) {
                LOG.info("新的节点{}已接入", event.getData().getPath());
                String newNode = event.getData().getPath();
                String newNodeIp = new String(event.getData().getData());
                // 通过newNodeIp获取边缘服务器的信息
                // 注册信息到中心节点上
                // 这里通过网络进行请求，其实也是在做一次测试，查看远程服务器是否符合版本要求，网络是否顺畅
                try {
                    EdgeNodeEntity remoteEdgeNodeInfo = getRemoteEdgeNodeInfo(newNodeIp);
                    // TODO: 校验版本号是否符合要求
                    // 以url作为唯一的key值进行存储，url不会重复
                    availableEdgeNodes.put(newNodeIp, remoteEdgeNodeInfo);
                } catch (Exception e) {
                    LOG.warn("远程边缘节点无法连接:{}", newNodeIp);
                    e.printStackTrace();
                }
            }
            else if(event.getType() == PathChildrenCacheEvent.Type.CHILD_REMOVED) {
                String removeNodeId = new String(event.getData().getData());
                LOG.info("节点{}已离线", event.getData().getPath());
                // 从列表中移除节点
                if(availableEdgeNodes.containsKey(removeNodeId)) {
                    LOG.info("从云中心节点删除注册的边缘节点{}", removeNodeId);
                    availableEdgeNodes.remove(removeNodeId);
                }
            }
        });
        try {
            watcher.start();
            LOG.info("节点监听器已启动");
            return watcher;
        } catch (Exception e) {
            LOG.warn("节点监听器启动失败");
            e.printStackTrace();
            return null;
        }
    }

    private EdgeNodeEntity getRemoteEdgeNodeInfo(String url) {
        if("".equals(url)) {
            LOG.error("要访问的地址不能为空");
            throw new IllegalArgumentException("url为空");
        }
        url = "http://" + url + "/edgeNode/info";
        EdgeNodeEntity nodeInfo
                = restTemplate.getForEntity(url, EdgeNodeEntity.class).getBody();
        LOG.info("接受到的信息{}：", nodeInfo);
        return nodeInfo;
    }

    /**
     * 获取全部的可用边缘服务节点
     * @return
     */
    public List<String> getAvailableEdgeNodes() {
        return new ArrayList<String>(availableEdgeNodes.keySet());
    }
}
